#!/usr/bin/env python
#
#       pypassport.py
#       
#       Copyright (c) 2009 yorks <stuyorks@gmail.com>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 3 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

import sys
import httplib
import time
import threading
import getpass
import socket
from xml.dom import minidom
#from string import Template  
import os

version = '0.0.11'
timeout = 5
command = ""
configFile = os.path.expanduser('~/.pypassport/config')
autoLogin = True

LoginMsg = """<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ClientLogon xmlns="http://tempuri.org/">
<userName>%s</userName>
<password>%s</password>
<userIp>%s</userIp>
<hostName>everest Linux</hostName>
<userMac>00:00:00:00</userMac>
<userAgent>pypassport</userAgent>
<isVisiteForeign>true</isVisiteForeign>
</ClientLogon>
</soap:Body>
</soap:Envelope>
"""
LogoutMsg = """<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<logout xmlns="http://tempuri.org/">
<userName>%s</userName>
<userIp>%s</userIp>
<isOutSide>true</isOutSide>
</logout>
</soap:Body>
</soap:Envelope>
"""
KeepMsg = """<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<keepUserActive xmlns="http://tempuri.org/">
<userName>%s</userName>
<isOutSide>true</isOutSide>
<userAgent>pypassport</userAgent>
</keepUserActive>
</soap:Body>
</soap:Envelope>
"""
GetFlowMsg = """<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetFlow xmlns="http://tempuri.org/">
<UserName>%s</UserName>
</GetFlow>
</soap:Body>
</soap:Envelope>
""" 
#check whether can reach the Passport Server and return Local IP
def checkNetwork_getIP():
    ###checkNetWork 
    socket.setdefaulttimeout(timeout)
    try:
        conn = httplib.HTTPConnection("auth.stu.edu.cn")
        conn.request('GET','/')
        resp = conn.getresponse()
    except Exception:
        print "Cannot reach the NC server"
        return False
    ### get local IP address
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    port = socket.getservbyname('http', 'tcp')
    s.settimeout(timeout)
    try:
        s.connect(("auth.stu.edu.cn", port))
    except socket.error, e:
         #print "Connection Error: %s" %e
         #print "Can not connet to STU passport Server"
         #print "Please check your network."
         s.close()
         return False
    except socket.time:
         print "Connot to STU passprot timeout..." 
         print "Please check the network."
         s.close()
         return False
         #sys.exit(1)
    ipAndPort = s.getsockname()
    ip = ipAndPort[0]
    s.close()
    return ip
#getTag value in XML 
def getTagText(root, tag):
    node = root.getElementsByTagName(tag)[0]
    rc = ""
    for node in node.childNodes:
        if node.nodeType in ( node.TEXT_NODE, node.CDATA_SECTION_NODE):
            rc = rc + node.data
    return rc


#password secure
def jiami(password):
    passlist = map((lambda x: ord(x)), password)
    passlist2 = []
    for i in passlist:
        if int(i)< 100:
            i = int(i) + 888
        passlist2.append(i)
    securePassword = ''
    for j in passlist2:
        securePassword = securePassword + str(j)
    securePassword = hex(long(securePassword))[2:-1]
    return securePassword

def jiemi(securePassword):
    securePassword = int(securePassword, 16)
    #print securePassword
    passlist = []
    for i in range(0, len(str(securePassword)),3):
        x = 0; y = 3
        x = x + i; y = y + i
        word = str(securePassword)[x:y]
        #print word
        if int(word) >= 800:
            word = int(word)-888
        passlist.append(int(word))
    passlist2 = map((lambda x: chr(x)), passlist)
    password = ''
    for j in passlist2:
        password = password + str(j)
    return password


#function login...
def login(account, password, ip):
    LoginMessage = LoginMsg%(account, password, ip)
    #print LoginMessage
    
    ###construct and send the header
    socket.setdefaulttimeout(timeout)
    try:
        p_webservice = httplib.HTTPS("auth.stu.edu.cn")
        p_webservice.putrequest("POST", "/netfilter/UserLogon.asmx")
        p_webservice.putheader("Host", "auth.stu.edu.cn")
        p_webservice.putheader("User-Agent", "Borland SOAP 1.2")
        p_webservice.putheader("Content-type", "text/xml; charset=\"UTF-8\"")
        p_webservice.putheader("Content-length", "%d" % len(LoginMessage))
        p_webservice.putheader("Cache-Control", "no-cache")
        p_webservice.putheader("Connection", "Keep-Alive")
        p_webservice.putheader("SOAPAction", "\"http://tempuri.org/ClientLogon\"")
        p_webservice.endheaders()
        p_webservice.send(LoginMessage)
    except socket.timeout:
        print "Connect to STU NC Server TIMEOUT when Login, Now exiting....OK"
        p_webservice.close()
        sys.exit(1)
    except:
        print "Network ERROR When Login....Please check your network configure"
        print "Now exiting...............OK!"
        p_webservice.close()
        sys.exit(1)
    
    ### get the response from server
    statuscode, statusmessage, header = p_webservice.getreply()
    #print "Response: ", statuscode, statusmessage
    #print "headers: ", header
    res = p_webservice.getfile().read(359)
    p_webservice.close()
    #print res
    xmldoc = minidom.parseString(res)
    root = xmldoc.documentElement
    tag = 'ClientLogonResult'
    loginResult = getTagText(root, tag)
    #print loginResult
    return loginResult #return the login code 
	
def getFlow(account):
    GetFlowMessage = GetFlowMsg%(account)
    #print GetFlowMessage
    
    #construct and send the header
    socket.setdefaulttimeout(timeout)
    try:
        p_webservice = httplib.HTTPS("auth.stu.edu.cn")
        p_webservice.putrequest("POST", "/netfilter/UserLogon.asmx")
        p_webservice.putheader("Host", "auth.stu.edu.cn")
        p_webservice.putheader("Content-type", "text/xml; charset=\"UTF-8\"")
        #p_webservice.putheader("Connection", "Keep-Alive")
        p_webservice.putheader("User-Agent", "Borland SOAP 1.2")
        p_webservice.putheader("Content-length", "%d" % len(GetFlowMessage))
        p_webservice.putheader("Cache-Control", "no-cache")
        p_webservice.putheader("SOAPAction", "\"http://tempuri.org/GetFlow\"")
        p_webservice.endheaders()
        p_webservice.send(GetFlowMessage)
    except socket.timeout:
        print 
        print "Connect to STU NC Server TIMEOUT when GetFlow, Now exiting....OK"
        print "Trying again...Please wait...."
        print "If you give up to trying, press 'q' to quit, More help Press 'Enter' !"
        p_webservice.close()
        return False
    except:
        print 
        print "Network ERROR when GetFlow....Please check your network configure"
        print "Trying again...Please wait...."
        print "If you give up to trying, press 'q' to quit, More help Press 'Enter' !"
        p_webservice.close()
        return False

    
    # get the response from Server
    statuscode, statusmessage, header = p_webservice.getreply()
    #print "Response: ", statuscode, statusmessage
    #print "headers: ", header
    res = p_webservice.getfile().read()
    p_webservice.close()
    #print res
    xmldoc = minidom.parseString(res)
    root = xmldoc.documentElement
    tag = 'DayFlow'
    dayflow = getTagText(root, tag)
    tag = 'DayFlowTotal'
    dayTotal = getTagText(root, tag)
    print "    DayFlow: %sM/%sM" % (dayflow, dayTotal)
    tag = 'MonthFlow'
    monthflow = getTagText(root, tag)
    tag = 'MonthFlowTotal'
    monthTotal = getTagText(root, tag)
    print "    MonthFlow: %sM/%sM" % (monthflow, monthTotal)
    return True
	
def keepActive(account, password, ip):
    KeepMessage = KeepMsg%(account)
    #print KeepMessage
    
    #construct and send the header
    socket.setdefaulttimeout(timeout)
    try:
        p_webservice = httplib.HTTPS("auth.stu.edu.cn")
        p_webservice.putrequest("POST", "/netfilter/UserLogon.asmx")
        p_webservice.putheader("Host", "auth.stu.edu.cn")
        #p_webservice.putheader("Connection", "Keep-Alive")
        p_webservice.putheader("User-Agent", "Borland SOAP 1.2")
        p_webservice.putheader("Content-type", "text/xml; charset=\"UTF-8\"")
        p_webservice.putheader("Content-length", "%d" % len(KeepMessage))
        p_webservice.putheader("Cache-Control", "no-cache")
        p_webservice.putheader("SOAPAction", "\"http://tempuri.org/keepUserActive\"")
        p_webservice.endheaders()
        p_webservice.send(KeepMessage)
    except socket.timeout:
        print 
        print "Connect to STU NC Server TIMEOUT when keepActive..."
        p_webservice.close()
        return False
    except:
        print 
        print "Network ERROR when keepActive....Please check your network configuration!"
        p_webservice.close()
        return False
        #sys.exit(1)
    
    # get the response from Server
    statuscode, statusmessage, header = p_webservice.getreply()
    #print "Response: ", statuscode, statusmessage
    #print "headers: ", header
    res = p_webservice.getfile().read(371)
    p_webservice.close()
    #print res
    xmldoc = minidom.parseString(res)
    root = xmldoc.documentElement
    tag = 'keepUserActiveResult'
    keepActive = getTagText(root, tag)
    if '100' == keepActive:
        print 
        print "You have logout, Now login again..."
        loginAgain = forceLogin(account, password, ip)
        if loginAgain == '222':
            print "OK...You have Login again! Press 'Enter' for more help"
            return True
        else:
            return False
    else:
        return True
	
def logout(account, ip):
    LogoutMessage = LogoutMsg%(account, ip)
    #print LogoutMessage
    
    #construct and send the header
    socket.setdefaulttimeout(timeout)
    try:
        p_webservice = httplib.HTTPS("auth.stu.edu.cn")
        p_webservice.putrequest("POST", "/netfilter/UserLogon.asmx")
        p_webservice.putheader("Host", "auth.stu.edu.cn")
        p_webservice.putheader("User-Agent", "Borland SOAP 1.2")
        p_webservice.putheader("Content-type", "text/xml; charset=\"UTF-8\"")
        p_webservice.putheader("Content-length", "%d" % len(LogoutMessage))
        p_webservice.putheader("Cache-Control", "no-cache")
        #p_webservice.putheader("Connection", "Keep-Alive")
        p_webservice.putheader("SOAPAction", "\"http://tempuri.org/logout\"")
        p_webservice.endheaders()
        p_webservice.send(LogoutMessage)
    except socket.timeout:
        print "Connect to STU NC Server TIMEOUT when logouting, Now exiting....OK"
        p_webservice.close()
        return False
    except:
        print "Network ERROR when logouting....Please check your network configure"
        print "Now exiting...............OK!"
        p_webservice.close()
        return False
    
    # get the response from server
    statuscode, statusmessage, header = p_webservice.getreply()
    #print "Response: ", statuscode, statusmessage
    #print "headers: ", header
    res = p_webservice.getfile().read()
    p_webservice.close()
    #print res
    return True
def helpMsg():
    print "===============Please follow the belove Command=============="
    print 
    print " -------------- 'f' for Lookup the Flow ---------------------"
    print " -------------- 'q' for Logout and Quit this program --------"
    print " -------------- 'e' for Exit this program  ------------------"
    print " -------------- 'w' for Which account you are using----------"
    print " -------------- 'a' for About this program ------------------"
    print " -------------- 'v' for Version printed----------------------"
    print " -------------- 'h' for Help (this HelpMessage)--------------"
def aboutMsg():
    print "=========================About==============================="
    print "|   pypassport is a program of STU Passport client, which   |"
    print "| based on python. It is a free software under the GPLv3.   |"
    print "| Thanks to my friend Leon, who give me the idea to write   |"
    print "| this program.                                             |"
    print "|   Report bugs to: yorks<stuyorks@gmail.com>               |"
    print "-------------------------------------------------------------"
def printVersion():
    print "========================Version Info========================="
    print "pypassport %s                                                " % version
    print "License: GNU GPL version 3 <http://gnu.org/licenses/gpl.html>"
    print "This is free software: you are free to change and redistribute it."
    print "There is NO WARRANTY, to the extent permitted by law.        "
    print 
    
def loopKeep(account, password, ip, sleeptime, *args):
    while True:
        if command == 'q':
            sys.exit(1)
        
        time.sleep(sleeptime)
        k = keepActive(account, password, ip)
        if not k:
           print "Trying again...Please wait...."
           print "If you give up to trying, press 'q' to quit, More help Press 'Enter' !"
           #sys.exit(1)

### If only keepActive, sometimes it can not reach the Internet will happen, but According to 
### http://auth.stu.edu.cn/campusAccount/searchIn3.aspx   It also at login status.
#### bug ???
### keepCheck will logout and then login to solve this problem....
def keepCheck(account, password, ip, sleeptime, *args):
    while True:
        if command == 'q':
            sys.exit(1)
        else:
            time.sleep(sleeptime)
            timeout = 1
            socket.setdefaulttimeout(timeout)
            try:
                conn = httplib.HTTPConnection("www.baidu.com")
                conn.request('GET','/')
                resp = conn.getresponse()
            except Exception:
                print "Cannot reach the Internet, retrying........",
                while True:
                    fLogin = forceLogin(account, password, ip)
                    if fLogin == '222':
                        #print "OK"
                        break


def loopCommand(account,ip):
    global command
    while 1:
        command = raw_input('>')
        if command == 'f':
            #print "your flow :"
            flowC = getFlow(account)
            #if not flowC:
                #sys.exit(1)
        elif command == 'q':
            logoutC = logout(account,ip)
            print "Logouting and Quiting......OK"
            sys.exit(1)
        elif command == 'e':
            print "Exiting........OK"
            sys.exit(1)
        elif command == 'w':
            print "you are using the account is :%s" % account
        elif command == 'a':
            aboutMsg()
        elif command == 'v':
            printVersion()
        elif command == 'h':
            helpMsg()
        else:
            helpMsg()
class MyThread(threading.Thread):
    def __init__(self, func, args, name = ''):
        threading.Thread.__init__(self)
        self.name = name
        self.func = func
        self.args = args
    def run(self):
        apply(self.func, self.args)
def forceLogin(account, password, ip):
    logoutR = False
    while not logoutR:
        logoutR = logout(account, ip)
    fLoginC = login(account, password, ip)
    return fLoginC

def getAccount():
    if os.path.isfile(configFile):
        fobjr = open(configFile, 'r', -1)
        all = fobjr.readlines()
        accountAndpassword = [ line.replace(os.linesep, '', 1) for line in all ]
        return accountAndpassword
    else:
        account = raw_input("Input account:")
        password = getpass.getpass("Input password:")
        accountAndpassword = (jiami(account), jiami(password))
        return accountAndpassword

def savePasswrod(account, password):
        if os.path.isdir(os.path.split(configFile)[0]):
            print "May be you have delete or modified the configfile, now setting it again!"
            fobjw = open(configFile, 'w', -1)
            fobjw.write(jiami(account) + os.linesep)
            fobjw.write(jiami(password) + os.linesep)
            fobjw.close()
            os.chmod(configFile, 0600)
            return True
        else:
            os.mkdir(os.path.split(configFile)[0])
            os.chmod(os.path.split(configFile)[0], 0700)
            fobjw = open(configFile, 'w', -1)
            fobjw.write(jiami(account) + os.linesep)
            fobjw.write(jiami(password) + os.linesep)
            fobjw.close()
            os.chmod(configFile, 0600)
            #print "You choose SAVE account and password, Next time you can login autoly!"
            return True

### Use the account which not in the config file to login
#def changeLogin(account, password, ip):
#    loginC = ''
#    while loginC != '222':
#        account = raw_input("Input account:")
#        password = getpass.getpass("Input password:")
#        loginC = login(account, password, ip)
#    return True
    
###check account,  password whether is right, and check whether have login with other IP
def checkLogin(account, password, ip):
    loginC = login(account, password, ip)
    time = 3
    while loginC != '222' and time <= 3 and time >= 1:
        if loginC == '000':
            print "Account, ERROR, Input again!"
            print "You have %d time(s) to try" % time
            account =  raw_input("Input account:")
            loginC = login(account, password, ip)
            time = time - 1
        elif loginC == '001':
            print "password ERROR, Input again!"
            print "You have %d time(s) to try" % time
            password = getpass.getpass("Input password:")
            loginC = login(account, password, ip)
            time = time -1
        elif loginC == '002':
            print "Account or IP are Occupied!!"
            print "Press 'L' for logout it and then Login,\n'C' for Change another Account to Login,\nelse to exit."
            f_login = raw_input( ":")
            if f_login == 'L' or f_login == 'l':
                loginC = forceLogin(account, password, ip)
                print loginC
            elif f_login == 'C' or f_login == 'c':
                account = raw_input("Input account:")
                password = getpass.getpass("Input password:")
                loginR = login(account, password, ip)
                #loginR = changeLogin(account, password, ip)
                loginInfo = [loginR, account, password]
                return loginInfo
            else:
                print "Now exit.......OK"
                sys.exit(1)
        elif loginC == '231':
            print "The account %s todays flow have used completed! Now exit.....OK" % account
            sys.exit(1)
        else:
           print "Unknow ERROR...Please try it late!!"
           sys.exit(1)
    if loginC == '222':
       loginInfo = [loginC, account, password]
       if os.path.isfile(configFile) and autoLogin:
           if cmp(account, jiemi(getAccount()[0])) != 0 or cmp(password, jiemi(getAccount()[1])) != 0:
               print "Your password has update in the congfig file!"
               savePasswrod(account, password)
               return loginInfo

           else:
               return loginInfo
       elif autoLogin:
           save = raw_input('Would you like to save the password for next time you can login autoly? y/n:')
           if save == 'Y' or save == 'y':
               savePasswrod(account, password)
               print "Saving the configure........OK"
               print "Next time you can login autoly...."
               return loginInfo
           else:
               print "Not Save the configure."
               return loginInfo
       else:
           print "Useing argv to login..."
       return loginInfo
    

def main():
    print 
    print " ==============Welcome use the pypassport v%s===============" % version
    ip = checkNetwork_getIP()
    if ip == 'False':
        print "Can not get your IP address, Please Check your network!"
        sys.exit(1)
    if len(sys.argv) == 3:
        account = sys.argv[1]
        password = sys.argv[2]
        global autoLogin
        autoLogin = False
    elif len(sys.argv) == 1:
        accountAndpass = getAccount()
        account = jiemi(accountAndpass[0])
        password = jiemi(accountAndpass[1])
    else:
        print "\nERROR, please follow the usage like this:"
        print "------------login with argv-------------"
        print "$ python pypassport.py account password\n"
        print "-------auto login with the config file--"
        print "$ python pypassport.py\n"
        sys.exit(1)
    loginR = checkLogin(account, password, ip)
    if loginR[0] == '222':
       account = loginR[1]
       password = loginR[2]
       loginR = True
    else:
       print loginR
       account = loginR[1]
       password = loginR[2]
       loginR = checkLogin(account, password, ip)
    if loginR:
            print 
            print " ==================:) Login Succeed! (:====================="
            print 
            print "Press 'f' for lookup the Flow, 'q' for logout and quit, 'h' for help!"
            #print account
            t_keepCheck = MyThread(keepCheck, (account, password, ip, 5))
            t_keep = MyThread(loopKeep, (account, password, ip, 60))
            t_command = MyThread(loopCommand, (account,ip))
            t_command.start()
            t_keepCheck.setDaemon(True)
            t_keepCheck.start()
            t_keep.setDaemon(True)
            t_keep.start()


if __name__ == '__main__': main()
